package com.xiangxiao.rpan.authority.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.xiangxiao.rpan.authority.constant.GlobalConsts;
import com.xiangxiao.rpan.authority.dao.TopicMapper;
import com.xiangxiao.rpan.authority.dao.UserMapper;
import com.xiangxiao.rpan.authority.dto.QueryUsersByTopicDto;
import com.xiangxiao.rpan.authority.dto.SaveTopicUserDto;
import com.xiangxiao.rpan.authority.dto.TopicUserRelatedDto;
import com.xiangxiao.rpan.authority.model.UserInfo;
import com.xiangxiao.rpan.authority.oauth.RpanRedisTokenStore;
import com.xiangxiao.rpan.authority.service.LoginService;
import com.xiangxiao.rpan.authority.utils.HttpServletRequestUtil;
import com.xiangxiao.rpan.authority.vo.TopicVo;
import com.xiangxiao.rpan.authority.vo.UserVo;
import com.xiangxiao.rpan.utils.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Projectname: LoginServiceImpl
 * @Author: xiangxiao
 * @Email: 573768011@qq.com
 * @Data:2023/7/11 23:49
 */
@Service
public class LoginServiceImpl implements LoginService {
  private static Logger log = LogManager.getLogger(LoginServiceImpl.class);
  @Autowired
  private StringRedisTemplate redisTemplate;
  @Autowired
  private TopicMapper topicMapper;
  @Autowired
  private UserMapper userMapper;
  @Autowired
  private SqlSessionFactory sqlSessionFactory;
  @Autowired
  private AuthorizationServerEndpointsConfiguration endpoints;
  @Override
  public boolean queryLoggedStatus() {
    String token = HttpServletRequestUtil.getToken();
    int isExpire = 0;
    if (!StringUtils.isBlank(token) && !StringUtils.isBlank(redisTemplate.opsForValue().get(token))) {
      isExpire = JSONObject.parseObject(redisTemplate.opsForValue().get(token), UserInfo.class).getUserVo().getExpired();
    };
    return isExpire != 0;
  }

  @Override
  public List<String> queryUserIdsByTopic(QueryUsersByTopicDto ueryUsersByTopicDto) {
    String topic = ueryUsersByTopicDto.getTopic();
    if (!StringUtils.isBlank(topic) && !StringUtils.isBlank(topic)) {
      return topicMapper.findUsersBytopic(topic);
    };
    return new ArrayList<String>();
  }

  @Override
  public List<TopicVo> queryAllAlarmTopics() {
    return topicMapper.getAllTopics();
  }

  @Override
  public List<UserVo> queryAllUsers() {
    return userMapper.findUserByAccount("");
  }

  @Override
  public List<Integer> batchUpdateTopicUsers(SaveTopicUserDto dto) {
    SimpleDateFormat sdf = new SimpleDateFormat(DateUtil.FORMAT_DATETIME_CN);
    List<Integer> ids = new ArrayList<>();
    List<TopicUserRelatedDto> newData = this.collectNewFields(dto);

    List<TopicUserRelatedDto> records = this.queryAllTopicRelatedUsers();
    List<TopicUserRelatedDto> oldData = this.collectOldFields(records);

//    Set<TopicUserRelatedDto> difference = this.getDifference(oldData, newData);
//    Set<TopicUserRelatedDto> intersection = this.getIntersection(oldData, newData);

    topicMapper.deleteByTopicId(dto.getTopicId());

    // 开启批量处理模式BATCH, 关闭自动提交事务的机制,改成全部数据一次性提交,更新完毕后再关闭sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
    // 对交集进行处理(新增)
    for (TopicUserRelatedDto insert: newData) {
      insert.setCreateTime(sdf.format(new Date()));
      Integer id = topicMapper.insertTopicUserRelated(insert);
      ids.add(id);
    };
    // 一次性提交事务
    sqlSession.commit();
    sqlSession.close();

    return ids;
  }

  private List<TopicUserRelatedDto> queryAllTopicRelatedUsers() {
    List<TopicUserRelatedDto> exsiteds = topicMapper.getllTopicRelatedUsers();
    return exsiteds;
  }

  private List<TopicUserRelatedDto> collectNewFields (SaveTopicUserDto dto) {
    List<Integer> ids = dto.getUserIds();

    List<TopicUserRelatedDto> topicDtos = ids.stream()
        .map(id -> {
          TopicUserRelatedDto topicDto = new TopicUserRelatedDto();
          topicDto.setTopicId(dto.getTopicId());
          topicDto.setUserId(id);
          return topicDto;
        })
        .collect(Collectors.toList());

    return topicDtos;
  }

  private List<TopicUserRelatedDto> collectOldFields(List<TopicUserRelatedDto> olds) {
    List<TopicUserRelatedDto> topicDtos = olds.stream()
        .map(old -> {
          TopicUserRelatedDto topicDto = new TopicUserRelatedDto();
          topicDto.setTopicId(old.getTopicId());
          topicDto.setUserId(old.getUserId());
          return topicDto;
        })
        .collect(Collectors.toList());
    return topicDtos;
  }

  //求差集, 即需要删除的数据
  private Set<TopicUserRelatedDto> getDifference(List<TopicUserRelatedDto> oldData,
                                                 List<TopicUserRelatedDto> newData) {
    Set<TopicUserRelatedDto> difference = new HashSet<>(oldData);
    difference.removeAll(newData);
    return difference;
  }

  // 求交集,即需要新增的数据
  private Set<TopicUserRelatedDto> getIntersection(List<TopicUserRelatedDto> oldData,
                                                   List<TopicUserRelatedDto> newData){
    Set<TopicUserRelatedDto> intersection = new HashSet<>(newData);
    intersection.removeAll(oldData);
    return intersection;
  }

  @Override
  public void logout(String token) {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null) {
      log.info("登录信息不存在,无法退出登录token={}", token);
      return;
    }
    // 删除token
    RpanRedisTokenStore tokenStore = (RpanRedisTokenStore) endpoints.getEndpointsConfigurer().getTokenStore();
    tokenStore.removeAccessToken(token);
    redisTemplate.opsForValue().getOperations().delete(token);

    AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
    String key = authenticationKeyGenerator.extractKey((OAuth2Authentication)authentication);

    String authToAccessKey = GlobalConsts.RPAN_AUTHORITY + ":" + "auth_to_access:" + key;
    redisTemplate.opsForValue().getOperations().delete(authToAccessKey);
  }

}
